package xing.application.service.page;

import com.fasterxml.jackson.core.type.TypeReference;
import com.querydsl.core.types.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.data.domain.*;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import xing.application.service.BaseService;
import xing.application.service.page.dto.AppVo;
import xing.application.service.page.dto.ContentItem;
import xing.application.service.page.dto.ContentMoveVo;
import xing.application.service.task.MoveContentTask;
import xing.domain.page.PageContentItem;
import xing.domain.page.PageModuleItem;
import xing.domain.page.QPageContentItem;
import xing.domain.page.QPageModuleItem;
import xing.domain.page.enums.ModuleType;
import xing.infrastructure.data.CallResult;
import xing.infrastructure.util.GenerateImage;
import xing.infrastructure.util.IdWorker;
import xing.infrastructure.util.JsonUtil;
import xing.infrastructure.util.StringUtil;
import xing.repository.page.PageContentItemRepository;
import xing.repository.page.PageModuleItemRepository;

import java.io.File;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.function.Predicate;

/********************************
 * 文 件 名 :PageContentItemService.java
 * 创 建 人：陈天龙
 * 日    期：2017/8/9
 * 描   述：模块内容业务处理类
 * 版 本 号：v1.0.0
 ********************************/
@Service
@CacheConfig(cacheNames = "pageContentItem")
public class ModuleContentService extends BaseService{

    @Autowired
    private PageContentItemRepository contentItemRepository;
    @Autowired
    private PageModuleItemRepository moduleItemRepository;
    @Autowired
    private MoveContentTask moveContentTask;
    @Autowired
    private RedisTemplate redisTemplate;

    private @Value("${web.upload-img-path}") String upload_path;

    private @Value("${web.main-url}") String mainUrl;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 描   述：新增模块内容
     * 创 建 人：陈天龙
     * 日    期：2017/8/9
     * @return CallResult
     */
    @Transactional(rollbackFor = Exception.class)
    public CallResult createContent(ContentItem contentItem) throws Exception{
        CallResult callResult = CallResult.error("失败");
        //根据模块类型区分业务逻辑
        switch (contentItem.getType()){
            case ModuleType.dingtalkISV:
                callResult = loadApps(contentItem);
                break;
            default:
                callResult = create(contentItem);
                break;
        }
        return callResult;
    }

    /**
     * 描    述：导入
     * 创 建 人：陈天龙
     * 日    期：2017/8/14
     * @param contentItem
     * @return CallResult
     */
    @Transactional(rollbackFor = Exception.class)
    public CallResult importContent(ContentItem contentItem) throws Exception{
        PageModuleItem pageModuleItem = new PageModuleItem();
        Timestamp now = new Timestamp(System.currentTimeMillis());
        //获取最大的sort并加1
        Long sort = moduleItemRepository.getMaxSort(contentItem.getModuleid());
        sort = (null == sort) ? 0L : (sort + 1L);
        BeanUtils.copyProperties(contentItem,pageModuleItem);
        pageModuleItem.setId(IdWorker.nextId());
        pageModuleItem.setSort(sort);
        pageModuleItem.setCreated(now);
        pageModuleItem.setReftype(0);
        pageModuleItem.setCompanycode(contentItem.getCorpId());

        moduleItemRepository.save(pageModuleItem);
        return CallResult.ok();
    }

    /**
     * 描   述：加载应用app
     * 创 建 人：陈天龙
     * 日    期：2017/8/11
     * @param contentItem
     * @return CallResult
     */
    private CallResult loadApps(ContentItem contentItem)throws Exception{
        long starTime = System.currentTimeMillis();
        /*
        每次登录加载一次,去redis中查找是否存在对应数据,如不存在则加入redis和数据库中,
        如存在,则比对appId,过滤出被删除以及新增的应用,进行对应的删除及新增操作.
         */
        ListOperations<String, PageContentItem> listOper = redisTemplate.opsForList();
        //获取应用集合
        List<AppVo> appVoList = JsonUtil.decode(contentItem.getAppJson(), new TypeReference<List<AppVo>>() { });
        List<PageContentItem> newApps = new ArrayList<PageContentItem>();
        Timestamp now = new Timestamp(System.currentTimeMillis());
        //将appVoList对应到contentItemList中
        if(null!=appVoList&&appVoList.size()>0) {
            for (AppVo appVo : appVoList) {
                PageContentItem pageContentItem = new PageContentItem();
                pageContentItem.setId(appVo.getAgentId());//暂定
                pageContentItem.setTarget(appVo.getAppId());
                pageContentItem.setImageurl(appVo.getIcon());
                pageContentItem.setTitle(appVo.getName());
                pageContentItem.setType(contentItem.getType());
                pageContentItem.setCompanycode(contentItem.getCorpId());
                pageContentItem.setCreated(now);
                pageContentItem.setUpdated(now);
                newApps.add(pageContentItem);
            }
        }
        //获取redis中的应用集合
        String key = contentItem.getType()+contentItem.getCorpId();
        try {
            List<PageContentItem> oldApps = listOper.range(key,0,-1);
            if(oldApps.size()>0){
                //删除不存在的应用
                oldApps.stream().filter(oldApp->(!newApps.contains(oldApp)
                        &&oldApp.getCompanycode().equals(contentItem.getCorpId()))).forEach(oldApp->{
                    try {
                        deleteContent(oldApp.getId(),contentItem.getCorpId());
                    } catch (Exception e) {
                        logger.error("删除应用"+oldApp.getTitle()+"失败",e);
                    }
                });
                //新增新的应用
                newApps.stream().filter(newApp->(!oldApps.contains(newApp)) && newApp.getCompanycode().equals(contentItem.getCorpId()))
                        .forEach(newApp-> saveIsv(newApp));
                listOper.getOperations().delete(key);
            }else{
                newApps.stream().forEach(newApp-> saveIsv(newApp));
            }
            if(newApps.size()>0)
                listOper.leftPushAll(key,newApps);
        } catch (Exception e) {
            logger.error("应用业务处理失败",e);
            listOper.getOperations().delete(key);
            throw e;
        }
        long endTime = System.currentTimeMillis();
        logger.info("加载应用总共耗费时间:"+(endTime - starTime)+"毫秒");
        return CallResult.ok();
    }

    /**
     * 描    述：保存应用
     * 创 建 人：陈天龙
     * 日    期：2017/9/2
     * @param newApp
     */
    private void saveIsv(PageContentItem newApp){
        try {
            deleteContent(newApp.getId(),newApp.getCompanycode());
            StringBuffer imageName = new StringBuffer();
            imageName.append(newApp.getCompanycode()).append("_").append(newApp.getId()).append("_").append(System.currentTimeMillis()).append(".png");
            String uploadPath = upload_path + File.separator + "content";
            String filename = GenerateImage.download(newApp.getImageurl(),new String(imageName),uploadPath);
            String imageurl = filename.replace(upload_path,mainUrl+"/image");
            newApp.setImageurl(imageurl);
            contentItemRepository.save(newApp);
        } catch (Exception e) {
            logger.error("保存应用失败",e);
        }
    }

    /**
     * 描   述：新增内容
     * 创 建 人：陈天龙
     * 日    期：2017/8/9
     * @param contentItem
     * @return CallResult
     */
    private CallResult create(ContentItem contentItem) throws Exception{
        /*
        新增内容需要同时插入2个表数据,page_module_item和page_content_item
         */
        PageContentItem pageContentItem = new PageContentItem();
        PageModuleItem pageModuleItem = new PageModuleItem();
        Timestamp now = new Timestamp(System.currentTimeMillis());

        BeanUtils.copyProperties(contentItem,pageContentItem);
        pageContentItem.setId(IdWorker.nextId());
        pageContentItem.setCreated(now);
        pageContentItem.setUpdated(now);
        pageContentItem.setCompanycode(contentItem.getCorpId());

        if(null != contentItem.getImageFile() && contentItem.getImageFile().length() > 0){
            try {
                String imageurl = uploadImage(contentItem.getCorpId(),pageContentItem.getId(),new String(contentItem.getImageFile()));
                pageContentItem.setImageurl(imageurl);
            } catch (Exception e) {
                logger.error("上传图片失败",e);
                return CallResult.error("上传图片失败");
            }
        }

        try {
            //获取最小的sort并减1
            Long sort = moduleItemRepository.getMinSort(contentItem.getModuleid());
            sort = (sort == null) ? 0L : (sort - 1);
            BeanUtils.copyProperties(contentItem,pageModuleItem);
            pageModuleItem.setId(IdWorker.nextId());
            pageModuleItem.setSort(sort);
            pageModuleItem.setContentid(pageContentItem.getId());
            pageModuleItem.setCreated(now);
            pageModuleItem.setReftype(0);
            pageModuleItem.setCompanycode(contentItem.getCorpId());

            contentItemRepository.save(pageContentItem);
            moduleItemRepository.save(pageModuleItem);
            //保存到缓存中
            String key = "content_"+pageContentItem.getCompanycode()+"_"+pageContentItem.getId();
            redisTemplate.opsForValue().set(key,pageContentItem);
        } catch (Exception e) {
            logger.error("新增保存失败",e);
            throw e;
        }
        return CallResult.ok();
    }
    /**
     * 描   述：上传图片
     * 创 建 人：陈天龙
     * 日    期：2017/9/12
     * @param corpId
     * @param contentId
     * @param imageFile
     * @return String
     */
    private String uploadImage(String corpId,Long contentId,String imageFile)throws Exception{
        logger.debug("图片文件不为空,进入保存图片方法");
        //保存图片
        StringBuffer imageName = new StringBuffer();
        String imageurl = new String();
        imageName.append(corpId).append("_").append(contentId).append("_").append(System.currentTimeMillis());
        String uploadPath = upload_path + File.separator + "content"+File.separator;
        String filename = GenerateImage.base64Generate(imageFile,uploadPath,imageName);
        imageurl = filename.replace(upload_path,mainUrl+"/image");
        return imageurl;
    }
    /**
     * 描   述：内容编辑
     * 创 建 人：陈天龙
     * 日    期：2017/9/5
     * @param contentItem
     * @return CallResult
     */
    @Transactional(rollbackFor = Exception.class)
    public CallResult editContent(ContentItem contentItem) throws Exception{
        Timestamp now = new Timestamp(System.currentTimeMillis());
        String key = "content_"+contentItem.getCorpId()+"_"+contentItem.getContentid();
        PageContentItem pageContentItem = (PageContentItem) redisTemplate.opsForValue().get(key);
        //删除缓存中的旧数据
        if(null != pageContentItem) {
            if(null != contentItem.getImageFile() && contentItem.getImageFile().length() > 0){
                String imageFile = pageContentItem.getImageurl();
                if(pageContentItem.getImageurl().indexOf(mainUrl+"/image") >= 0){
                    imageFile = imageFile.replace(mainUrl+"/image",upload_path);
                }
                GenerateImage.delImage(imageFile);
                try {
                    String imageurl = uploadImage(contentItem.getCorpId(),contentItem.getContentid(),new String(contentItem.getImageFile()));
                    contentItem.setImageurl(imageurl);
                } catch (Exception e) {
                    logger.error("上传图片失败",e);
                    return CallResult.error("上传图片失败");
                }
            }else{
                contentItem.setImageurl(pageContentItem.getImageurl());
            }
            redisTemplate.delete(key);
        }
        QPageContentItem qPageContentItem = QPageContentItem.pageContentItem;
        List<Path<?>> paths = new ArrayList<>();
        List<Object> values = new ArrayList<>();
        paths.add(qPageContentItem.imageurl);values.add(contentItem.getImageurl());
        paths.add(qPageContentItem.target);values.add(contentItem.getTarget());
        paths.add(qPageContentItem.title);values.add(contentItem.getTitle());
        paths.add(qPageContentItem.updated);values.add(now);
        queryFactory.update(qPageContentItem).set(paths,values)
                .where(qPageContentItem.id.longValue().eq(contentItem.getContentid())
                        ,qPageContentItem.companycode.stringValue().eq(contentItem.getCorpId())).execute();

        return CallResult.ok();
    }

    /**
     * 描   述：删除内容
     * 创 建 人：陈天龙
     * 日    期：2017/8/10
     * @param   id  模块内容表id
     * @return CallResult
     */
    @Transactional(rollbackFor = Exception.class)
    public CallResult deleteContent(Long id,String corpId)throws Exception{
        //删除缓存中的数据
        String key = "content_"+corpId+"_"+id;
        PageContentItem pageContentItem = (PageContentItem) redisTemplate.opsForValue().get(key);
        if(null != pageContentItem) {
            String imageFile = pageContentItem.getImageurl();
            if(!StringUtils.isEmpty(imageFile)){
                if(pageContentItem.getImageurl().indexOf(mainUrl+"/image") >= 0){
                    imageFile = imageFile.replace(mainUrl+"/image",upload_path);
                }
                GenerateImage.delImage(imageFile);
            }

            redisTemplate.delete(key);
        }else{
            PageContentItem contentItem = contentItemRepository.findByCompanycodeAndId(corpId,id);
            if(null != contentItem) {
                String imageFile = contentItem.getImageurl();
                if(!StringUtils.isEmpty(imageFile)){
                    if (contentItem.getImageurl().indexOf(mainUrl + "/image") >= 0) {
                        imageFile = imageFile.replace(mainUrl + "/image", upload_path);
                    }
                    GenerateImage.delImage(imageFile);
                }

            }
        }
        //删除内容的同时删除关系表的相关数据
        contentItemRepository.deleteAllByIdAndCompanycode(id,corpId);

        QPageModuleItem qPageModuleItem = QPageModuleItem.pageModuleItem;
        Long count = queryFactory.delete(qPageModuleItem).where(qPageModuleItem.contentid.longValue().eq(id),qPageModuleItem.companycode.stringValue().eq(corpId)).execute();
        return CallResult.ok(count);
    }

    /**
     * 描   述：移除内容
     * 创 建 人：陈天龙
     * 日    期：2017/8/10
     * @param id  模块内容关系表id
     * @return CallResult
     */
    @Transactional(rollbackFor = Exception.class)
    public CallResult removeContent(Long id)throws Exception{
        moduleItemRepository.delete(id);
        return CallResult.ok();
    }

    /**
     * 描   述：内容列表
     * 创 建 人：陈天龙
     * 日    期：2017/8/10
     * @param companyCode  企业号
     * @param moduleType 模块类型
     * @return CallResult
     */
    public CallResult query(String companyCode,String moduleType,Integer pageNum,Integer pageSize){
        //查询出所有数据,并按创建时间降序排列
        Pageable pageable = new PageRequest(pageNum-1, pageSize, new Sort(Sort.Direction.DESC,"Created"));
        if(moduleType.equals("dingtalkISV")){
            pageable = new PageRequest(pageNum-1, pageSize, new Sort(Sort.Direction.ASC,"Target"));
        }
        PageContentItem pageContentItem = new PageContentItem();
        pageContentItem.setCompanycode(companyCode);
        pageContentItem.setType(moduleType);
        Page<PageContentItem> pageContentItemList = contentItemRepository.findAll(Example.of(pageContentItem,ExampleMatcher.matchingAll()),pageable);
        return CallResult.ok(pageContentItemList);
    }

    /**
     * 描   述：内容详情
     * 创 建 人：陈天龙
     * 日    期：2017/8/10
     * @param id  内容id
     * @return CallResult
     */
    public CallResult getContentDetail(Long id,String corpId){
        String key = "content_"+corpId+"_"+id;
        PageContentItem pageContentItem = (PageContentItem) redisTemplate.opsForValue().get(key);
        if(null == pageContentItem) {
            pageContentItem = contentItemRepository.findByCompanycodeAndId(corpId, id);
            redisTemplate.opsForValue().set(key,pageContentItem);
        }
        return CallResult.ok(pageContentItem);
    }

    /**
     * 描   述：上移下移,采用异步的方式保存数据库(待商榷)
     * 创 建 人：陈天龙
     * 日    期：2017/8/10
     * @param contentMoveVo
     * @return CallResult
     */
    @Transactional(rollbackFor = Exception.class)
    public CallResult moveContent(ContentMoveVo contentMoveVo)throws Exception{
        /*
        上移和下移,都是把互换位置的两个内容的sort互换
         */
        moveContentTask.setContentMoveVo(contentMoveVo);
        moveContentTask.setQueryFactory(queryFactory);

        moveContentTask.move();
        return CallResult.ok();
    }

    /**
     * 描   述：内容置顶
     * 创 建 人：陈天龙
     * 日    期：2017/8/10
     * @param id  内容id
     * @param moduleId 模块id
     * @return CallResult
     */
    @Transactional(rollbackFor = Exception.class)
    public CallResult topContent(Long id,Long moduleId)throws Exception{
        Long sort = null;
        try {
            //获取最小的sort并减1
            QPageModuleItem source = QPageModuleItem.pageModuleItem;
            sort = moduleItemRepository.getMinSort(moduleId)-1;
            queryFactory.update(source).where(source.id.longValue().eq(id)).set(source.sort,sort).execute();
        } catch (Exception e) {
            logger.error("置顶失败",e);
            throw e;
        }
        return CallResult.ok(sort);
    }
}
